// jtwain.cpp

#include <windows.h>
#include <tchar.h>
#include "time.h"  
#include "twain.h"     // Standard TWAIN header.
#include <string.h>
#include "../jtwain.h"
#include "tiffio.h" 
#include <unistd.h>


  
// The following macros serve a useful purpose: in the event of failure, they
// allow the executing thread to skip past a region of code that should not be
// executed because of that failure, while allowing that thread to perform
// necessary cleanup prior to leaving the current function. For example, a
// thread has created a window, opened the source manager connected to that
// window, and is attempting to open a source. But for some reason, the source
// can't be opened. The thread must back out of the function, first closing
// the source manager and then destroying the window. The thread must not
// execute any other code. The macros make that happen.

// BLOCK_BEGIN(x) and BLOCK_END(x) surround a block of code where a failure
// might occur, such as code that attempts to open a source. The x is a
// placeholder for a number that makes it relatively easy to distinguish
// between nested blocks, but serves no other purpose. Those two macros are
// implemented as a do { ... } while (0); construct. That construct executes
// the code between do and while exactly once. If a thread needs to
// prematurely exit from that block, it calls the EXIT_CURRENT_BLOCK macro, a
// break statement.

#define BLOCK_BEGIN(x)      do {
#define BLOCK_END(x)        } while (0);
#define EXIT_CURRENT_BLOCK  break;

// g_hinstDLL holds this DLL's instance handle. It is initialized in response
// to the DLL_PROCESS_ATTACH message. This handle is passed to CreateWindow()
// when a window is created, just before opening the data source manager.
FILE *oplog;

static HINSTANCE g_hinstDLL;


// g_hLib holds the handle of the TWAIN_32.DLL library. It is initialized in
// response to the DLL_PROCESS_ATTACH message. This handle is used to obtain
// the DSM_Entry() function address in the DLL and (if not 0) to free the DLL
// library in response to a DLL_PROCESS_DETACH message.

static HMODULE g_hLib;
static  TW_IDENTITY datasourcelist[20];
// g_pDSM_Entry holds the address of function DSM_Entry() in TWAIN_32.DLL. If
// this address is 0, either TWAIN_32.DLL could not be loaded or there is no
// DSM_Entry() function in TWAIN_32.DLL.

static DSMENTRYPROC g_pDSM_Entry;

// g_AppID serves as a TWAIN identity structure that uniquely identifies the
// application process responsible for making calls to function DSM_Entry().

static TW_IDENTITY g_AppID;
static TW_ENTRYPOINT g_DSM_Entry   = {0}; 
// Function prototype for JTwainException exception-throwing helper function.

static void throwJTE (JNIEnv *env, const char *msg);

// Function prototypes for image transfer helper functions.

static int xferDIB8toImage  (LPBITMAPINFOHEADER lpbmih, JNIEnv *env,const char * filename);
static int xferDIB24toImage (LPBITMAPINFOHEADER lpbmih, JNIEnv *env,const char * filename);
int bufferedImageTransfer(TW_IDENTITY srcID, TW_IMAGEINFO ii,char *fullname );
int bufferedImageTransferCompressed(TW_IDENTITY srcID, TW_IMAGEINFO ii,char *fullname );
int nativeImageTransfer(TW_IDENTITY srcID, TW_IMAGEINFO ii,char *fullname , JNIEnv *env );
void RotateRight(const char *file);
// ===========================================================================
// DllMain
//
// DLL entry point. Four messages are sent from Windows to this function:
//
// 1) DLL_PROCESS_ATTACH
//
// This message is sent when the DLL is first mapped into a process's address
// space.
//
// 2) DLL_THREAD_ATTACH
//
// This message is sent when a thread is created in the DLL's owner process.
//
// 3) DLL_THREAD_DETACH
//
// This message is sent when a thread created in the DLL's owner process is
// terminated in any fashion other than by calling TerminateThread().
//
// 4) DLL_PROCESS_DETACH
//
// This message is sent when the DLL is unmapped from a process's address
// space -- unless some thread calls TerminateProcess().
//
// Arguments:
//
// hinstDLL    - DLL instance handle
// fdwReason   - reason for calling DllMain()
// lpvReserved - additional data that may be present during DLL_PROCESS_ATTACH
//               and DLL_PROCESS_DETACH messages
//
// Return:
//
// TRUE if DLL's initialization (during DLL_PROCESS_ATTACH) was successful, or
// FALSE if otherwise. This return value is only recognized in response to the
// DLL_PROCESS_ATTACH message.
// ===========================================================================
 

 
#pragma argsused
BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{

   if (fdwReason == DLL_PROCESS_ATTACH)
   {
       // Save instance handle for later access in other functions.

       g_hinstDLL = hinstDLL;

       // Create a buffer for holding the Windows directory path.

       char szPath [150]; // Probably only 140 is needed, but why not be safe?
      
       // Retrieve the path of the Windows directory.

      // GetWindowsDirectory (szPath, 128);

       // Obtain number of characters copied into the buffer.

      // int iLen = lstrlen (szPath);

       // Path ends in a backslash character if the directory is the root.
       // Otherwise, path does not end in backslash. In that case, we must
       // append a backslash character to the path.

     //  if (iLen != 0 && szPath [iLen-1] != '\\')
      //     lstrcat (szPath, "\\");

       // Append TWAIN_32.DLL to the path. This is the 32-bit TWAIN DLL that
       // we need to communicate with.

     //  lstrcat (szPath, "TWAIN_32.DLL");

       // If the TWAIN_32.DLL file exists in the path (which is determined by
       // opening and closing that file), attempt to load TWAIN_32.DLL into
       // the calling process's address space.

      // OFSTRUCT ofs;
       //if (OpenFile (szPath, &ofs, OF_EXIST) != -1)
         

       // Report failure if TWAIN_32.DLL cannot be loaded and terminate the
       // JTWAIN DLL.
       g_hLib = LoadLibrary ("TWAIN_32.DLL");
       if (g_hLib == 0)
       {
           MessageBox (0, "Unable to open TWAIN_32.DLL", "JTWAIN", MB_OK);
           return FALSE;
       }

       // Attempt to retrieve DSM_Entry() function address.

         g_pDSM_Entry = (DSMENTRYPROC) GetProcAddress (g_hLib, "DSM_Entry");
     
	 
	 
        
     
	   
	   
	   
	   
	   
       // Report failure if DSM_Entry() function not found in TWAIN_32.DLL
       // and terminate the JTWAIN DLL.

       if (g_pDSM_Entry == 0)
       {
           MessageBox (0, "Unable to fetch DSM_Entry address", "JTWAIN", 
                       MB_OK);
           return FALSE;
       }
  
          oplog =  fopen("log.txt","a");
      
       		
       // Initialize g_AppID. This structure is passed to DSM_Entry() in each
       // function call.

       g_AppID.Id = 0;
       g_AppID.Version.MajorNum = 2;
       g_AppID.Version.MinorNum = 0;
       g_AppID.Version.Language = TWLG_ENGLISH_USA;
       g_AppID.Version.Country = TWCY_USA;

       lstrcpy (g_AppID.Version.Info, "2.0.9");

       g_AppID.ProtocolMajor = TWON_PROTOCOLMAJOR;
       g_AppID.ProtocolMinor = TWON_PROTOCOLMINOR;
      // g_AppID.SupportedGroups = DG_CONTROL | DG_IMAGE;

      
	    g_AppID.SupportedGroups = DF_APP2|DG_CONTROL | DG_IMAGE;

       lstrcpy (g_AppID.Manufacturer, "Amskape");
       lstrcpy (g_AppID.ProductFamily, "Java Scanner");
       lstrcpy (g_AppID.ProductName, "JTWAIN");
   }
   else
   if (fdwReason == DLL_PROCESS_DETACH)
   {
       // If the TWAIN_32.DLL library was loaded, remove it from memory.

       if (g_hLib != 0)
           FreeLibrary (g_hLib);
          
           fclose(oplog);
   }

   return TRUE;
}

int  getAllSources(JNIEnv *env, jobject obj)
{

	    
           int i =0;
           TW_IDENTITY Source;
           memset(&Source, 0, sizeof(TW_IDENTITY));
		   TW_UINT16 twrc;

           twrc = (*g_pDSM_Entry)(
								&g_AppID,
								0,
								DG_CONTROL,
								DAT_IDENTITY,
								MSG_GETFIRST,
								(TW_MEMREF) &Source);

		  switch (twrc)
		  {
		    case TWRC_SUCCESS:
		        datasourcelist[i++] = Source;
			break;

		    case TWRC_FAILURE:
			    throwJTE (env, "Datasource not found");
			break;

		    case TWRC_ENDOFLIST:
			return i;
			break;
			//return;
			
		  }
			
			// get the rest of the sources
		  do
		  {
			memset(&Source, 0, sizeof(TW_IDENTITY));

			twrc = (*g_pDSM_Entry)(
			  &g_AppID,
			  0,
			  DG_CONTROL,
			  DAT_IDENTITY,
			  MSG_GETNEXT,
			  (TW_MEMREF) &Source);

			switch (twrc)
			{
			case TWRC_SUCCESS:
			
			datasourcelist[i++] = Source;
			  break;

			case TWRC_FAILURE:
			   throwJTE (env, "Failed to get the rest of the data source info!");
			  return i;
			  break;

			case TWRC_ENDOFLIST:
			   return i ;
			  break;
			}
		  }
		  while (TWRC_SUCCESS == twrc); 	  
			  
			//;
			 
			 
			 
			 return i; 
        

}

pTW_IDENTITY   getSource(JNIEnv *env, jobject obj, const char * ProductName)

{

           int result;
           TW_UINT16 twsetrc;
           int i =0;
           TW_IDENTITY Source;
		   pTW_IDENTITY        m_pDataSource; 
           memset(&Source, 0, sizeof(TW_IDENTITY));
		   TW_UINT16 twrc;

           twrc = (*g_pDSM_Entry)(
								&g_AppID,
								0,
								DG_CONTROL,
								DAT_IDENTITY,
								MSG_GETFIRST,
								(TW_MEMREF) &Source);
		  switch (twrc)
		  {
		    case TWRC_SUCCESS:
			result = strcmp(Source.ProductName, ProductName);
			     if( result == 0)
				 {
				     
                       m_pDataSource		= &Source;	
                         
					   
				    return m_pDataSource;		
				  
				 }
		        //datasourcelist[i++] = Source;
			break;

		    case TWRC_FAILURE:
			    throwJTE (env, "Failed to get the data source info!");
				return NULL;
			break;

		    case TWRC_ENDOFLIST:
			     return NULL;
			break;
			//return;
			
		  }
			
			// get the rest of the sources
		  do
		  {
			memset(&Source, 0, sizeof(TW_IDENTITY));

			twrc = (*g_pDSM_Entry)(
			  &g_AppID,
			  0,
			  DG_CONTROL,
			  DAT_IDENTITY,
			  MSG_GETNEXT,
			  (TW_MEMREF) &Source);
			switch (twrc)
			{
			case TWRC_SUCCESS:
			 result = strcmp(Source.ProductName, ProductName);
			     if( result == 0)
				 {
				       
				      m_pDataSource		= &Source;
                      return m_pDataSource;					  
				 }
			
			  break;

			case TWRC_FAILURE:
			   throwJTE (env, "Failed to get the rest of the data source info!");
			  return NULL;
			  break;

			case TWRC_ENDOFLIST:
			    return NULL;
			  break;
			}
		  }
		  while (TWRC_SUCCESS == twrc); 	  
			  
			//;
			 
			 
			 
			 return NULL;
        

}
JNIEXPORT jobjectArray JNICALL Java_com_bimager_jtwain_jtwain_getSourceList
  (JNIEnv *env, jobject obj){
	
	
	jobjectArray ret ;  
  
      //int i =0;  
  
   
   //create windows 
	 
	 HWND hwnd = CreateWindow ("STATIC",
                             "",
                             WS_POPUPWINDOW,
                             CW_USEDEFAULT,
                             CW_USEDEFAULT,
                             CW_USEDEFAULT,
                             CW_USEDEFAULT,
                             HWND_DESKTOP,
                             0,
                             g_hinstDLL,
                             0);

   // If window could not be created, throw exception.

   if (hwnd == 0)
   {
       throwJTE (env, "Unable to create private window "
                 "(selectSourceAsDefault)");
       return;
   }

   // Ensure that the default data source's dialog box does not disappear
   // behind other windows, which can be very disconcerting to the user. We do
   // that by making the hwnd -- created above and passed to DSM_Entry() below
   // -- the handle of the topmost window.

  // SetWindowPos (hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE);

   TW_UINT16 rc;
   TW_IDENTITY srcID;

   BLOCK_BEGIN(1)

   // Open the data source manager.

   rc = (*g_pDSM_Entry) (&g_AppID,
                         0,
                         DG_CONTROL,
                         DAT_PARENT,
                         MSG_OPENDSM,
                         (TW_MEMREF) &hwnd);

   // If data source manager could not be opened, throw exception. Because the
   // exception is not actually thrown until execution returns to Java, we
   // first exit current block to destroy previously-created window.

   if (rc != TWRC_SUCCESS)
   {
       throwJTE (env, "Unable to open data source manager "
                 "(selectSourceAsDefault)");
       EXIT_CURRENT_BLOCK
   }

   // Display the "Select Source" dialog box for selecting a data source.

  
   jclass stringClass = (*env)->FindClass(env, "java/lang/String");
                  int i =  getAllSources(env, obj);
			    //  nos = i;
			
			    
			   
			    if(i > 0){
                                
                                 
                                  jobjectArray row = (*env)->NewObjectArray(env, i, stringClass, 0);
							
						int j;
						for(j=0;j< i;j++) {  
                                                 
						     (*env)->SetObjectArrayElement(env, row, j, (*env)->NewStringUTF(env, datasourcelist[j].ProductName));
   }


                  		ret = row;			
					//  return ret;
			        }
  
  
  
  
  

   // If failure occurred, prepare to throw an exception, which is only thrown
   // after this function ends.

  

   // Close the data source manager.

   (*g_pDSM_Entry) (&g_AppID,
                    0,
                    DG_CONTROL,
                    DAT_PARENT,
                    MSG_CLOSEDSM,
                    (TW_MEMREF) &hwnd);

   BLOCK_END(1)

   // Destroy the window.

   DestroyWindow (hwnd);
	 
	 
   
	
	
	
	  
			
	//load DSM DLL and Load DSM_Entry Procedure

 

return ret;			
			
                        	
	
	   
	
}
/**********************************************************
 * * Convert a FIX32 value into a floating point value.
**********************************************************/
float FIX32ToFloat (TW_FIX32 fix32)
{
float floater;
floater = (float)fix32.Whole + (float)fix32.Frac / 65536.0;
return floater;
}

/**********************************************************
 * * Convert a floating point value into a FIX32.
**********************************************************/
TW_FIX32 FloatToFix32 (float floater)
{
TW_FIX32 Fix32_value;
TW_INT32 value = (TW_INT32) (floater * 65536.0 + 0.5);
Fix32_value.Whole = value >> 16;
Fix32_value.Frac = value & 0x0000ffffL;
return (Fix32_value);
}
 
// ===========================================================================
// Java_net_javajeff_jtwain_JTwain_acquire
// ===========================================================================

#pragma argsused
JNIEXPORT jobjectArray  JNICALL Java_com_bimager_jtwain_jtwain_aqcuireImage
  (JNIEnv *env, jobject obj,jstring productname,jstring imagename,jstring imagedir)
{
   // Create a static window whose handle is passed to DSM_Entry() when we
   // open the data source manager.
 const char *_productname = (*env)->GetStringUTFChars(env, productname, NULL);
 const char *filename_ = (*env)->GetStringUTFChars(env, imagename, NULL);
 const char *imagedirectory = (*env)->GetStringUTFChars(env, imagedir, NULL);
 jclass classString = (*env)->FindClass(env, "java/lang/String");
 jobjectArray files = (*env)->NewObjectArray(env, 0, classString, NULL);
 
 uint32 mode = 1;


  //strftime (filename,80,"scannerFile%F%I_%M_%p.tiff",today);
 // MessageBox (0, filename, "JTWAIN", MB_OK);
						

 
     if (NULL == _productname)
         return NULL;

   HWND hwnd = CreateWindow ("STATIC",
                             "",
                             WS_POPUPWINDOW,
                             CW_USEDEFAULT,
                             CW_USEDEFAULT,
                             CW_USEDEFAULT,
                             CW_USEDEFAULT,
                             HWND_DESKTOP,
                             0,
                             g_hinstDLL,
                             0);

   // If window could not be created, throw exception. Because the exception
   // is not actually thrown until execution returns to Java, we must return
   // a value -- (jobject) 0 was chosen to represent Image null. This value
   // will not be seen in the Java code because of the exception.

   if (hwnd == 0)
   {
       throwJTE (env, "Unable to create private window (acquire)");
       return (jobject) 0;
   }

   // Ensure that the default data source's dialog box does not disappear
   // behind other windows, which can be very disconcerting to the user. We do
   // that by making the hwnd -- created above and passed to DSM_Entry() below
   // -- the handle of the topmost window.

   SetWindowPos (hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE);

   TW_UINT16 rc;
   jobject image = 0;

   BLOCK_BEGIN(1)

   // Open the data source manager.

   rc = (*g_pDSM_Entry) (&g_AppID,
                         0,
                         DG_CONTROL,
                         DAT_PARENT,
                         MSG_OPENDSM,
                         (TW_MEMREF) &hwnd);

   // If data source manager could not be opened, throw exception. Because the
   // exception is not actually thrown until execution returns to Java, we
   // first exit current block to destroy previously-created window and return
   // a value (which isn't seen in the Java code).

   if (rc != TWRC_SUCCESS)
   {
       throwJTE (env, "Unable to open data source manager (acquire)");
       EXIT_CURRENT_BLOCK
   }
   if((g_AppID.SupportedGroups & DF_DSM2) == DF_DSM2)
	{
				g_DSM_Entry.Size = sizeof(TW_ENTRYPOINT);
				// do a MSG_GET to fill our entrypoints
				rc = (*g_pDSM_Entry)(&g_AppID,  0, DG_CONTROL, DAT_ENTRYPOINT, MSG_GET,(pTW_ENTRYPOINT)&g_DSM_Entry);
				if(TWRC_SUCCESS != rc)
				{
					 throwJTE (env, "DG_CONTROL / DAT_ENTRYPOINT / MSG_GET Failed");
					EXIT_CURRENT_BLOCK
				}
	}	 
   BLOCK_BEGIN(2)

   // Get the default data source's name.

  
			
   TW_IDENTITY srcID;
   pTW_IDENTITY psrcID;
   ZeroMemory (&srcID, sizeof(srcID));

   psrcID = getSource(env, obj,_productname );
   // If failure occurred, prepare to throw an exception, which is only thrown
   // after this function ends.

   if (psrcID == NULL)
   {
       throwJTE (env, "Unable to obtain default data source name (acquire)");
       EXIT_CURRENT_BLOCK
   }
   srcID = *psrcID;
   // Open the default data source.

   rc = (*g_pDSM_Entry) (&g_AppID,
                         0,
                         DG_CONTROL,
                         DAT_IDENTITY,
                         MSG_OPENDS,
                         &srcID);

   // If default data source could not be opened, throw exception. Because the
   // exception is not actually thrown until execution returns to Java, we
   // first exit current block to close data source manager and destroy the
   // previously-created window.

   if (rc != TWRC_SUCCESS)
   {
       throwJTE (env, "Unable to open default data source (acquire)");
       EXIT_CURRENT_BLOCK
   }

   BLOCK_BEGIN(3)
   
           
           //capability negotiation starts 
    TW_CAPABILITY twCap;   
    pTW_ONEVALUE  pval;   
    
    twCap.hContainer = GlobalAlloc(GHND, sizeof(TW_ONEVALUE));   
    pval = (pTW_ONEVALUE)GlobalLock(twCap.hContainer);  
    twCap.Cap        = CAP_XFERCOUNT;   
    twCap.ConType    = TWON_ONEVALUE;   
    pval->ItemType   = TWTY_UINT16;   
    pval->Item       = (TW_UINT32)-1;   
    rc = (*g_pDSM_Entry) (&g_AppID, &srcID, DG_CONTROL, DAT_CAPABILITY, MSG_SET,   
                      (TW_MEMREF)&twCap);   
    if (rc != TWRC_SUCCESS)
   {
       throwJTE (env, "Unable to open default data source (acquire)");
       EXIT_CURRENT_BLOCK
   }
  //  UNTwainError("Negotiate XFERCOUNT", rc);   
    // Set buffer memory transfer   
    twCap.Cap        = ICAP_XFERMECH;   
    twCap.ConType    = TWON_ONEVALUE;   
    pval->ItemType   = TWTY_UINT16;   
    pval->Item       = TWSX_MEMORY;   
    rc = (*g_pDSM_Entry) (&g_AppID, &srcID, DG_CONTROL, DAT_CAPABILITY, MSG_SET,   
                       (TW_MEMREF)&twCap);   
    if (rc != TWRC_SUCCESS)
   {
       throwJTE (env, "Unable to open default data source (acquire)");
       EXIT_CURRENT_BLOCK
   } 
    // Set pixel flavor   
    twCap.Cap        = ICAP_PIXELFLAVOR;   
    twCap.ConType    = TWON_ONEVALUE;   
    pval->ItemType   = TWTY_UINT16;   
    pval->Item       = TWPF_CHOCOLATE;   
    rc = (*g_pDSM_Entry) (&g_AppID, &srcID, DG_CONTROL, DAT_CAPABILITY, MSG_SET,   
                       (TW_MEMREF)&twCap);   
    if (rc != TWRC_SUCCESS)
   {
       throwJTE (env, "Unable to open default data source (acquire)");
       EXIT_CURRENT_BLOCK
   } 
   
    
    GlobalUnlock(twCap.hContainer);   
    GlobalFree(twCap.hContainer); 
   
    
    TW_CAPABILITY twCapability;
    TW_INT16 rc;
    twCapability.Cap = ICAP_COMPRESSION; //Fill in capability of interest
    twCapability.ConType = TWON_DONTCARE16;
    twCapability.hContainer = NULL;
    rc = (*g_pDSM_Entry)(&g_AppID,
                       &srcID,
                       DG_CONTROL,
                       DAT_CAPABILITY,
                       MSG_GET,
                       (TW_MEMREF)&twCapability);
    if (rc == TWRC_SUCCESS)
    {
         switch (twCapability.ConType)
         {
            //-----ENUMERATION
            case TWON_ENUMERATION:
                fputs("TWON_ENUMERATION",oplog);
                break;
            case TWON_ONEVALUE:
                 fputs("TWON_ONEVALUE",oplog);
            break;
            case TWON_RANGE:
                 fputs("TWON_ONEVALUE",oplog);
            break;
            
            case TWON_ARRAY:
                 fputs("TWON_ONEVALUE",oplog);
             break;
            
         }     
     } 
     else {
              //Capability MSG_GET Failed check Condition Code
          } 

   
    
    
     //capability negotiation Ends 
    
    
   TW_USERINTERFACE ui;
   ui.ShowUI = TRUE;
   ui.ModalUI = FALSE;
   ui.hParent = hwnd;

   // Enable the default data source.

   rc = (*g_pDSM_Entry) (&g_AppID,
                         &srcID,
                         DG_CONTROL,
                         DAT_USERINTERFACE,
                         MSG_ENABLEDS,
                         &ui);

   // If default data source could not be enabled, throw exception. Because
   // the exception is not actually thrown until execution returns to Java, we
   // first exit current block to close data source, close data source
   // manager and destroy the previously-created window.

   if (rc != TWRC_SUCCESS)
   {
       throwJTE (env, "Unable to enable default data source (acquire)");
       EXIT_CURRENT_BLOCK
   }
  
   // Begin the event-handling loop. Data transfer takes place in this loop.

   MSG msg;
   TW_EVENT event;
   TW_PENDINGXFERS pxfers;

   while (GetMessage ((LPMSG) &msg, 0, 0, 0))
   {
      // Each window message must be forwarded to the default data source.

      event.pEvent = (TW_MEMREF) &msg;
      event.TWMessage = MSG_NULL;

      rc = (*g_pDSM_Entry) (&g_AppID,
                            &srcID,
                            DG_CONTROL,
                            DAT_EVENT,
                            MSG_PROCESSEVENT,
                            (TW_MEMREF) &event);

      // If the message does not correspond to a data source event, we must
      // dispatch it to the appropriate Windows window.

      if (rc == TWRC_NOTDSEVENT)
      {             
          TranslateMessage ((LPMSG) &msg);
          DispatchMessage ((LPMSG) &msg);
          continue;
      }

      // If the default data source is requesting that the data source's
      // dialog box be closed (user pressed Cancel), we must break out of the
      // message loop.

      if (event.TWMessage == MSG_CLOSEDSREQ)
          break;

      // If the default data source is requesting that it is ready to begin
      // the data transfer, we must perform that transfer.

      if (event.TWMessage == MSG_XFERREADY)
      {
          // Obtain information about the first image to be transferred.
	  
          // Perform the transfer.
      
          int        bPendingXfers = 1;
          TW_UINT16   twrc          = TWRC_SUCCESS;
          //find number of tranfer
          TW_PENDINGXFERS pendxfers;
	  memset( &pendxfers, 0, sizeof(pendxfers) );
	  twrc =(*g_pDSM_Entry) (&g_AppID,
				&srcID,
				DG_CONTROL,
				DAT_PENDINGXFERS,
				MSG_ENDXFER,
				(TW_MEMREF) &pendxfers);
		 
          if(TWRC_SUCCESS == twrc)
		  {
			
			if(0 == pendxfers.Count)
			{
			  // nothing left to transfer, finished.
			  bPendingXfers = 0;
			}
		  }
		  else
		  {
			bPendingXfers = 0;
		  }
          
	  files = (*env)->NewObjectArray(env, pendxfers.Count, classString, NULL);
          int i=0;
        
          int transfer=0;
          while(bPendingXfers)
		{
	   
                      //update image info for every transfers
                TW_IMAGEINFO ii;
                rc = (*g_pDSM_Entry) (&g_AppID,
                                      &srcID,
                                      DG_IMAGE,
                                      DAT_IMAGEINFO,
                                      MSG_GET,
                                      (TW_MEMREF) &ii);

                // If unable to obtain image information ...
                 
                if (rc == TWRC_FAILURE)
                {
                    // Cancel all transfers.

                    (*g_pDSM_Entry) (&g_AppID,
                                     &srcID,
                                     DG_CONTROL,
                                     DAT_PENDINGXFERS,
                                     MSG_RESET,
                                     (TW_MEMREF) &pxfers);

                    // Throw exception upon return to Java and break out of event
                    // loop.

                    throwJTE (env, "Unable to obtain image information (acquire)");
                    break;
                }
                fprintf(oplog,"tansfer %d\n",++transfer);
                fputs("----Image Info----\n",oplog);
                fprintf(oplog,"ImageWidth : %d\n",ii.ImageWidth);
                fprintf(oplog,"ImageLength :%d\n",ii.ImageLength);
                fprintf(oplog,"SamplesPerPixel : %d\n",ii.SamplesPerPixel);
                fprintf(oplog,"BitsPerSample :%d\n",ii.BitsPerSample);
                fprintf(oplog,"BitsPerPixel :%d\n",ii.BitsPerPixel);
                fprintf(oplog,"Planar : %d\n",ii.Planar);
                fprintf(oplog,"PixelType : %d\n",ii.PixelType);
                fprintf(oplog,"Compression :%d\n",ii.Compression);
                
                  
                 int nBytePerRow = (((ii.ImageWidth * ii.BitsPerPixel)+7)/8);
 
                 fprintf(oplog,"nBytePerRow :%d\n",nBytePerRow);
               
                
                      
                            char *fullname = (char *)malloc(strlen(filename_)+strlen(imagedirectory)+4);
                            char filename[strlen(filename_)+3];
                            sprintf(filename,"f%d",i);
                            strcpy (fullname,imagedirectory);
                           
			    strncat(filename,filename_,strlen(filename_));
                            strncat(fullname,filename,strlen(filename));
                      
                              fprintf(oplog,"Uncompressed Buffered Transfer");
                            switch(mode)
                            {
                                case 1://buffered
                                        if(  bufferedImageTransfer(srcID,ii,fullname)){
                                             (*env)->SetObjectArrayElement(env, files, i, (*env)->NewStringUTF(env, filename));
                                              i++; 
                                        }
                                    break;
                                case 2://native
                                default:
                                    
                                         if(  nativeImageTransfer(srcID,ii,fullname,env)){
                                                (*env)->SetObjectArrayElement(env, files, i, (*env)->NewStringUTF(env, filename));
                                                 i++; 
                                             }
                                    break;  
                                
                                
                            }
                        
                             
                        
                        
                   TW_PENDINGXFERS pendxfers;
		   memset( &pendxfers, 0, sizeof(pendxfers) );
		   twrc =(*g_pDSM_Entry) (&g_AppID,
					  &srcID,
					  DG_CONTROL,
					  DAT_PENDINGXFERS,
					  MSG_ENDXFER,
					  (TW_MEMREF) &pendxfers);
		 
		  if(TWRC_SUCCESS == twrc)
		  {
			
			if(0 == pendxfers.Count)
			{
			  // nothing left to transfer, finished.
			  bPendingXfers = 0;
			}
		  }
		  else
		  {
			bPendingXfers = 0;
		  }              
                        
                        
                        
                        
                        
                        
                        
                        
                        
                   
                    fputs("----Image Transfer  Completed----\n",oplog);
                   // fclose(oplog);
  }
		  
        
            
 
        //  TIFFClose(tif); 
           
          // Cancel all remaining transfers.

          (*g_pDSM_Entry) (&g_AppID,
                           &srcID,
                           DG_CONTROL,
                           DAT_PENDINGXFERS,
                           MSG_RESET,
                           (TW_MEMREF) &pxfers);

          // Convert TWRC_XFERDONE to TWRC_SUCCESS so that appropriate value
          // is returned.
           image=(jobject) 0;
          rc = TWRC_SUCCESS;

          break;
      }
   }

   // Disable the data source.

   (*g_pDSM_Entry) (&g_AppID,
                    &srcID,
                    DG_CONTROL,
                    DAT_USERINTERFACE,
                    MSG_DISABLEDS,
                    &ui);

   BLOCK_END(3)

   // Close the data source.

   (*g_pDSM_Entry) (&g_AppID,
                    0,
                    DG_CONTROL,
                    DAT_IDENTITY,
                    MSG_CLOSEDS,
                    &srcID);

   BLOCK_END(2)

   // Close the data source manager.

   (*g_pDSM_Entry) (&g_AppID,
                    0,
                    DG_CONTROL,
                    DAT_PARENT,
                    MSG_CLOSEDSM,
                    (TW_MEMREF) &hwnd);

   BLOCK_END(1)

   // Destroy window.

   DestroyWindow (hwnd);
    return  files;//(*env)->NewStringUTF(env, filename_);

  // return (rc == TWRC_SUCCESS) ? image : (jobject) 0;
}
//native tansfer
int nativeImageTransfer(TW_IDENTITY srcID, TW_IMAGEINFO ii,char *fullname , JNIEnv *env )
{
    TW_MEMREF hImg = 0;
    TW_UINT16   twrc          = TWRC_SUCCESS;
    uint32 success= 0;
    twrc = (*g_pDSM_Entry) (&g_AppID,
			    &srcID,
			    DG_IMAGE,
			    DAT_IMAGENATIVEXFER,
			    MSG_GET,
			    (TW_MEMREF)&hImg);
    if(TWRC_XFERDONE == twrc)
       {
	 // -Here we get a handle to a DIB. Save it to disk as a bmp. 
	 // -After saving it to disk, I could open it up again using FreeImage
	 // if I wanted to do more transforms on it or save it as a different format.
	 LPBITMAPINFOHEADER lpbmi        = (LPBITMAPINFOHEADER) GlobalLock( hImg );
	 if(0 == lpbmi)
	   {
	      return 0;   
	  }
          if (ii.BitsPerPixel == 8 )
          {
               xferDIB8toImage (lpbmi, env,fullname);
              
                    GlobalUnlock(hImg);
		  GlobalFree(hImg);
		  lpbmi = 0;
                  success = 1;          
           }
           else if (ii.BitsPerPixel == 24 )
		{
                   xferDIB24toImage (lpbmi, env,fullname);
			 
                    GlobalUnlock(hImg);
		  GlobalFree(hImg);
		  lpbmi = 0;
                  success = 1;          
                          
		 }
           else 
           {
               
               success = 0;
               
           }
			  
			  
			
		 
		 

		
        }
    else
    {
        
         success = 0;
    }
    return success;
}





//Buffered Memmory
int bufferedImageTransfer(TW_IDENTITY srcID, TW_IMAGEINFO ii,char *fullname)
{
    TW_UINT16   twrc          = TWRC_SUCCESS;
    uint32 success= 0,hasNextBuf = 0 ;
    int nBytePerRow = (((ii.ImageWidth * ii.BitsPerPixel)+7)/8);
 
    TW_UINT16 rc;
    TW_SETUPMEMXFER iMem;
    HANDLE          hBuf; 
    unsigned char TW_HUGE *buf;  
    TW_IMAGEMEMXFER iBuf; 
    TIFF * tif;
                          // open the output TIFF image for writing
    if ((tif = TIFFOpen(fullname, "w")) == NULL)
           {
              return 0;   
           }
                        
    UINT32 w = ii.ImageWidth;
    UINT32 h = ii.ImageLength;
    TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, w);
    TIFFSetField(tif, TIFFTAG_IMAGELENGTH, h);
    TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE,8);
    if(ii.SamplesPerPixel == 1){
    TIFFSetField(tif, TIFFTAG_COMPRESSION,COMPRESSION_LZW);
    TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
    }
    else{
        
    TIFFSetField(tif, TIFFTAG_COMPRESSION,  COMPRESSION_PACKBITS);
    TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
     }
    TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, ii.SamplesPerPixel);
    TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
    TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_NONE);
    TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
    TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP,1);
                           
    twrc = (*g_pDSM_Entry) (&g_AppID,
                             &srcID,
                             DG_CONTROL,
                             DAT_SETUPMEMXFER,
                             MSG_GET,   
                            (TW_MEMREF)&iMem);  
     hBuf = GlobalAlloc(GHND, iMem.Preferred);   
     buf = (unsigned char TW_HUGE *)GlobalLock(hBuf);   
                       
     iBuf.Memory.Flags  = TWMF_APPOWNS | TWMF_POINTER;   
     iBuf.Memory.Length = iMem.Preferred;   
     iBuf.Memory.TheMem = buf;   
     iBuf.Compression   = TWON_DONTCARE16;   
     iBuf.BytesPerRow   = TWON_DONTCARE32;   
     iBuf.Columns       = TWON_DONTCARE32;   
     iBuf.Rows          = TWON_DONTCARE32;   
     iBuf.XOffset       = TWON_DONTCARE32;   
     iBuf.YOffset       = TWON_DONTCARE32;   
     iBuf.BytesWritten  = TWON_DONTCARE32;   
     hasNextBuf = 1; 
                          //tranfering image
     uint32 scanlinesize = TIFFScanlineSize(tif);
     fprintf(oplog,"TIFF Scan line size : %d\n",scanlinesize);
     uint32 strip=0;
     unsigned int x ;
     while (hasNextBuf == 1)   
           {   
               fputs("----Image Transfer starts----\n",oplog);
               rc = (*g_pDSM_Entry) (&g_AppID,
                                     &srcID,    
                                     DG_IMAGE, 
                                     DAT_IMAGEMEMXFER, 
                                     MSG_GET,   
                                     (TW_MEMREF)&iBuf);   
               if(rc == TWRC_SUCCESS || TWRC_XFERDONE == rc)
                  {
                     fputs("----Image Transfer Success----\n",oplog);
                     fputs("----IMAGEMEMXFER----\n",oplog);
                     fprintf(oplog,"BytesPerRow; :%d\n",iBuf.BytesPerRow);
                     fprintf(oplog,"Columns; : %d\n",iBuf.Columns);
                     fprintf(oplog,"Rows; :%d\n",iBuf.Rows);
                     fprintf(oplog,"XOffset; :%d\n",iBuf.XOffset);
                     fprintf(oplog,"YOffset; : %d\n",iBuf.YOffset);
                     fprintf(oplog,"BytesWritten; : %d\n",iBuf.BytesWritten);
               
                     char* pbuf = (char*)(iBuf.Memory.TheMem);
               
                     for(x= 0; x < iBuf.Rows; x++)
                         {
                           TIFFWriteEncodedStrip( tif, strip++,pbuf,nBytePerRow);
                           pbuf += iBuf.BytesPerRow;  
                         }
                  
                     free(pbuf);
                     iBuf.Memory.TheMem = buf;    
                
                
                
                     if(TWRC_XFERDONE == rc)
                        {
                         
                          TIFFClose(tif);
                          hasNextBuf = 0;  
                          success= 1;
                          GlobalUnlock(hBuf);   
                          GlobalFree(hBuf);  


                         }
                
                   }
                   else if(rc == TWRC_CANCEL || TWRC_FAILURE == rc)
                           {
                                    hasNextBuf = 0;   
                                    success= 0;
                                    GlobalUnlock(hBuf);   
                                    GlobalFree(hBuf);  
                                     TIFFClose(tif);
                            }
            
              
           } 
               
    
    
     return success;
}

// ===========================================================================
// throwJTE
// ===========================================================================

static void throwJTE (JNIEnv *env, const char *msg)
{
   // Return class object for the net.javajeff.jtwain.JTwainException class.

   jclass clazz = (*env)->FindClass (env,"com/bimager/jtwain/JTwainException");

   // If class not found, display message via Windows message box and
   // terminate the JVM. Otherwise, throw a new JTwainException object once
   // execution leaves native code and returns to Java.

   if (clazz == 0)
   {
         MessageBox (0, msg, "BImager", MB_OK);
        (*env)->FatalError (env,"Could not find net.javajeff.jtwain.JTwainException");
   }
   else 
        (*env)->ThrowNew (env,clazz, msg);
}

// ===========================================================================
// xferDIB8toImage
// ===========================================================================
void WriteHexString(FILE *fptr,char *s)
{
   unsigned int i,c;
   char hex[3];

   for (i=0;i<strlen(s);i+=2) {
      hex[0] = s[i];
      hex[1] = s[i+1];
      hex[2] = '\0';
      sscanf(hex,"%X",&c);
      putc(c,fptr);
   }
}
static int xferDIB8toImage (LPBITMAPINFOHEADER lpbmih, JNIEnv *env,const char * filename)
{
  int width = lpbmih->biWidth;

   int height = lpbmih->biHeight; // height < 0 if bitmap is top-down
   if (height < 0)
       height = -height;

	   
	   
	TIFF * tif;
	 if ((tif = TIFFOpen(filename, "w")) == NULL)
				{
					 return;
		}   
	         UINT32 w = lpbmih->biWidth;
		 UINT32 h = lpbmih->biHeight;
		 TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, w);
		 TIFFSetField(tif, TIFFTAG_IMAGELENGTH, h);
		 TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
		 TIFFSetField(tif, TIFFTAG_COMPRESSION, 1);
		 TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
		 TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1);
		 TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, 1);
		 TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
		 TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_NONE);
		 TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);		
		 

   
	   
   // Create Java-based integer pixels array to pass to the MemoryImageSource
   // constructor.

   int * pixelsArray ;
         pixelsArray = (int *)malloc (width );

   //(*env)->NewIntArray (env,width * height);
  

   // Populate the pixels array.

   int *palette = (int *) lpbmih + sizeof(BITMAPINFOHEADER);

   int numColors;

   if (lpbmih->biClrUsed > 0)
       numColors = lpbmih->biClrUsed;
   else
       numColors = 1 << lpbmih->biBitCount;

   unsigned char *bitmap = (unsigned char *) lpbmih +
                           sizeof(BITMAPINFOHEADER) +
                           numColors * sizeof(RGBQUAD);

   int padBytes = (4-width%4)%4; // Each pixel occupies 1 byte (palette index)
                                 // and the number of row bytes is a multiple of
                                 // 4.

  

   int rowBytes = width+padBytes;
   int row;
   int col;

     unsigned char * pdst = (char*) malloc (width); 



   for ( row = 0; row < height; row++){
   
   
          memset(pdst,0,(size_t)(width));
       
        for ( col= 0; col < width; col++)
        {
             // Extract color information for pixel and build an equivalent
             // Java pixel for storage in the Java-based integer array.

             int pixel = 0xff000000 |
                         palette [bitmap [rowBytes*row+col]];
           
             // Store the pixel in the array at the appropriate index.
                
            // pixelsArray [col] =  pixel;
                pdst[col]   = bitmap[rowBytes*row+col];
             	
        }
		TIFFWriteScanline(tif, pdst, row, 0);
		
		
		
		
}

		

 TIFFClose(tif);

   return 1;
}



static int xferDIB24toImage (LPBITMAPINFOHEADER lpbmih, JNIEnv *env,const char * filename)
{
   
   // Obtain the image's width and height -- both in pixels -- to pass to the
   // MemoryImageSource constructor.

    
    int width = lpbmih->biWidth;

   int height = lpbmih->biHeight; // height < 0 if bitmap is top-down
   TIFF * tif;
   // open the output TIFF image for writing
   if ((tif = TIFFOpen(filename, "w")) == NULL)
	{
		 return 0;
	}
        UINT32 w = lpbmih->biWidth;
	UINT32 h = lpbmih->biHeight;
	TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, w);
	TIFFSetField(tif, TIFFTAG_IMAGELENGTH, h);
	TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
	TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_PACKBITS);
	TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
	TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3);
	TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
	TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_NONE);
	TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);		
		 
   
   
   
  // if (height < 0)
    //   height = -height;

  
   
   // Populate the pixels array.

   unsigned char *bitmap = (unsigned char *) lpbmih +
                           sizeof(BITMAPINFOHEADER);
   
   unsigned char * pdst = (char*) malloc (w * 3);
   
   
   
   // We set the strip size of the file to be size of one row of pixels
    TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, width*3));
   
        int row;
        int col;
        UINT32 src_index;
        UINT32 dst_index;
 
	UINT32 bitcount = lpbmih->biBitCount;
        UINT32 bytecount = bitcount / 8;





   for ( row= 0; row < height; row++)
   {
   
        memset(pdst,0,(size_t)(w * 3));
        for ( col= 0; col < width; col++)
        {
		
		
		        src_index = (h - row - 1) * width * bytecount
                                          + col * bytecount;
                dst_index = col * 3;
                pdst[dst_index++] = bitmap[src_index+2];
                pdst[dst_index++] = bitmap[src_index+1];
                pdst[dst_index]   = bitmap[src_index];
		
		
		    
        }
		TIFFWriteScanline(tif, pdst, row, 0);
}
 TIFFClose(tif);
  

   return 1;
}

JNIEXPORT void JNICALL Java_com_bimager_jtwain_jtwain_saveTiff
  (JNIEnv *env, jobject  obj, jstring path, jstring file,jint _width,jint _height, jintArray _pixels,jint samples,jint bitspersample)
{
      
            
                 
  
      const char *filename = (*env)->GetStringUTFChars(env, file, NULL);
      int i,width = _width;
      int height = _height; // height < 0 if bitmap is top-down
   
      TIFF* outtif = TIFFOpen(filename, "w"); 
           //set tags from tiff    
              if (outtif  == NULL)
	{
		 return ;
	}        
                      TIFFSetField(outtif, TIFFTAG_IMAGEWIDTH,width);
                      TIFFSetField(outtif, TIFFTAG_IMAGELENGTH,height);
                      TIFFSetField(outtif, TIFFTAG_BITSPERSAMPLE, bitspersample);
                         if(samples == 1){
    TIFFSetField(outtif, TIFFTAG_COMPRESSION,COMPRESSION_LZW);
    TIFFSetField(outtif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
    }
    else{
        
    TIFFSetField(outtif, TIFFTAG_COMPRESSION,  COMPRESSION_PACKBITS);
    TIFFSetField(outtif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
     }      
                      TIFFSetField(outtif, TIFFTAG_SAMPLESPERPIXEL,samples);
                      TIFFSetField(outtif, TIFFTAG_ROWSPERSTRIP, 1);
                      TIFFSetField(outtif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
                      TIFFSetField(outtif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_NONE);
                      TIFFSetField(outtif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
                      
                      uint8 * image = (uint8*) _TIFFmalloc (width * samples);
                        int row;
        int col;
        UINT32 src_index;
        UINT32 dst_index;
 
	UINT32 bitcount = bitspersample*samples;
        UINT32 bytecount = bitcount / 8;

        jint  * pixels = (*env)->GetIntArrayElements(env, _pixels, NULL);


                for ( row= 0; row < height; row++)
			{
							
			i = 0;
			//j=0;
			for ( col= 0; col < width; col++)
				{
                            
                            if(samples == 3){
					src_index= (row*width+col)*samples;				
				image[i++]= pixels[src_index]& 0xff;
				image[i++]= pixels[src_index+1]& 0xff;
				image[i++]= pixels[src_index+2]& 0xff;
                            }
                            else
                            {
                                
                                src_index= (row*width+col)*samples;				
				image[i++]= pixels[src_index]& 0xff; 
                            }
				}
			TIFFWriteScanline(outtif, image, row, 0);			
			}
                 
            
                    
		         _TIFFfree(pixels);
			 _TIFFfree(image);
                                       
	TIFFClose(outtif);
    
    
   
   
   
   
        
        
        
        
    
}